package com.ecoolex.chinadutyfree.retailer.core.manager;

import com.ecoolex.chinadutyfree.retailer.core.CoreConstant;
import com.ecoolex.chinadutyfree.retailer.core.dao.PaymentOrderDAO;
import com.ecoolex.chinadutyfree.retailer.entity.PaymentOrder;
import com.ecoolex.chinadutyfree.retailer.entity.PaymentOrder.Column;
import com.ecoolex.chinadutyfree.retailer.entity.PaymentOrderExample;
import com.ecoolex.chinadutyfree.retailer.entity.PaymentOrderExample.Criteria;
import com.ecoolex.chinadutyfree.retailer.entity.PaymentOrderRecord;
import com.ecoolex.chinadutyfree.retailer.enums.PaymentOrderRecordOperType;
import com.ecoolex.chinadutyfree.retailer.enums.PaymentOrderRecordResultStatus;
import com.ecoolex.chinadutyfree.retailer.enums.PaymentOrderStatus;
import com.ecoolex.chinadutyfree.retailer.pojo.QueryPaymentOrderDTO;
import com.ecoolex.framework.common.enums.ResultCode;
import com.ecoolex.framework.common.pojo.Page;
import com.ecoolex.framework.common.pojo.Pagination;
import com.ecoolex.framework.common.pojo.Sorting;
import com.ecoolex.framework.common.pojo.Sortings;
import com.ecoolex.framework.common.util.BigDecimalUtil;
import com.ecoolex.framework.common.util.BizAssert;
import com.ecoolex.framework.common.util.Check;
import com.ecoolex.framework.common.util.DateUtil;
import com.ecoolex.framework.modularize.api.service.config.SystemConfig;
import com.ecoolex.union.pay.retailer.core.external.UnionPayService;
import com.ecoolex.union.pay.retailer.core.external.pojo.unionpay.UnionPayOrderNotifyResult;
import com.ecoolex.union.pay.retailer.core.external.pojo.unionpay.UnionPayOrderQueryResult;
import com.ecoolex.union.pay.retailer.core.external.pojo.unionpay.UnionPayRefundNotifyResult;
import com.ecoolex.union.pay.retailer.core.external.pojo.unionpay.UnionPayRefundResult;
import com.ecoolex.union.pay.retailer.enums.PaymentType;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PaymentOrderManager {
private static final Logger log = LoggerFactory.getLogger(PaymentOrderManager.class);
	
    @Autowired
    private PaymentOrderDAO dao;
    
    @Autowired
    private PaymentOrderRecordManager paymentOrderRecordManager;
    
    @Autowired
    private UnionPayService unionPayService;
    
    @Autowired
    private DirectMailGoodsOrderManager directMailGoodsOrderManager;
    
    @Autowired
	private SystemConfig systemConfig;
    
    private static final String TYPE_UNION_PAY_RETAILER_ORDER = "UPRO";
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public String pay(Integer type,String orderNo,String currencyType,Integer userId,
    	BigDecimal realPrice,Date orderCreateTime,
    	HttpServletRequest req, HttpServletResponse resp,String createUser) {
		// 订单超时时间 (分钟)
		String minutesStr = systemConfig.get(CoreConstant.CONFIG_CHINADUTYFREE_RETAILER_ORDER_AUTO_CANCEL_MINUTES);
		//计算支付超时时间,动态计算目前订单还剩余的支付时间
		int minutes = Integer.valueOf(minutesStr);
		int second = minutes*60-10;
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(orderCreateTime);
		calendar.add(Calendar.SECOND, second);
		String payTimeout = new SimpleDateFormat("yyyyMMddHHmmss").format(calendar.getTime());
		// 取支付订单
    	PaymentOrder paymentOrder = getByBizTypeAndOrderNo(TYPE_UNION_PAY_RETAILER_ORDER,orderNo);
    	if(Check.isNull(paymentOrder)) {
    		//创建支付订单
    		paymentOrder = new PaymentOrder();
    		paymentOrder.setBizType(TYPE_UNION_PAY_RETAILER_ORDER);
    		paymentOrder.setOrderNo(orderNo);
    		if(Check.notNull(type)) {
    			if(type.equals(PaymentType.UNION_PAY_WAP.real())) {
    				paymentOrder.setPaymentType(PaymentType.UNION_PAY_WAP.describe());
    			}else if(type.equals(PaymentType.NIHAO_PAY.real())){
    				paymentOrder.setPaymentType(PaymentType.NIHAO_PAY.describe());
    			}
    		}else {
    			//不传默认银联wap支付
    			paymentOrder.setPaymentType(PaymentType.UNION_PAY_WAP.describe());
    		}
    		paymentOrder.setCurrencyType(currencyType);
    		paymentOrder.setUserId(userId);
    		paymentOrder.setPaymentPrice(realPrice);
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_APPLY.real());
    		paymentOrder.setDeleted(PaymentOrder.NOT_DELETED);
    		paymentOrder.setCreateUser(createUser);
    		paymentOrder.setCreateTime(Date.from(Instant.now()));
    		paymentOrder.setUpdateUser(createUser);
    		paymentOrder.setUpdateTime(Date.from(Instant.now()));
            dao.insert(paymentOrder);
    	}else {
    		//失败才能重新发起支付
    		BizAssert.isTrue(paymentOrder.getStatus().equals(PaymentOrderStatus.PAY_FAILED.real()) || 
    			paymentOrder.getStatus().equals(PaymentOrderStatus.PAY_APPLY.real()),
    			ResultCode.BIZ.build(10), "订单已支付,无法发起支付");
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_APPLY.real());
    		updatePaymentOrder(paymentOrder,createUser);
    	}
    	//记录支付日志
    	PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder, PaymentOrderRecordOperType.ORDER_APPLY);
    	log.setTransAmount(paymentOrder.getPaymentPrice());
    	try {
    		String txnAmt = BigDecimalUtil.convertToString(BigDecimalUtil.dollarToCent(paymentOrder.getPaymentPrice()), 0);
    		String html = unionPayService.prepay(paymentOrder.getOrderNo(),txnAmt,payTimeout ,req,resp);
    		//将生成的html写到浏览器中完成自动跳转打开银联支付页面；这里调用signData之后，将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改，如果修改会导致验签不通过
    		//resp.getWriter().write(html);
    		log.setTransStartTime(paymentOrder.getCreateTime());
    		log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
    		return html;
    	}catch (Exception e) {
    		log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
    		log.setErrorMessage(e.getMessage());
    		//修改支付订单为支付失败
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_FAILED.real());
    		updatePaymentOrder(paymentOrder,createUser);
    		PaymentOrderManager.log.error("发起支付失败",e);
    		BizAssert.throwBizException("发起支付失败");
    		return null;
		}finally {
			paymentOrderRecordManager.add(log, createUser);
		}
	}
    
    public void orderNotify(HttpServletRequest req, HttpServletResponse response, String createUser) throws Exception{
		UnionPayOrderNotifyResult notifyResult = unionPayService.parseOrderNotifyResult(req,response);
		PaymentOrder paymentOrder = getByBizTypeAndOrderNo(TYPE_UNION_PAY_RETAILER_ORDER,notifyResult.getOrderId());
		BizAssert.isTrue(Check.notNull(paymentOrder),ResultCode.VERIFY.build(10), "支付订单不存在");
		//检查订单支付状态，并且记录操作日志
		PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder, PaymentOrderRecordOperType.ORDER_NOTIFY);
		if(notifyResult.getStatus().equalsIgnoreCase("SUCCESS")) {
			log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(Long.valueOf(notifyResult.getTxnAmt()))));
			log.setTransNo(notifyResult.getQueryId());
			log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", notifyResult.getTxnTime()));
			log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
		}else {
			log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
		}
		paymentOrderRecordManager.add(log, createUser);
		//更新订单
		updatePaymentOrderAndBizOrder(paymentOrder,log,createUser);
		//返回给银联服务器http 200  状态码
		response.getWriter().print("ok");
    }
    
    public List<PaymentOrder> queryPayApplyTimeout(Date timeout,int size){
    	PaymentOrderExample example = new PaymentOrderExample();
    	Criteria criteria = example.createCriteria();
    	criteria.andStatusEqualTo(PaymentOrderStatus.PAY_APPLY.real())
    			.andUpdateTimeLessThanOrEqualTo(timeout)
    			.andDeletedEqualTo(PaymentOrder.NOT_DELETED);
    	example.orderBy(Column.updateTime.asc());
    	example.limit(size);
    	return dao.selectByExample(example);
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public PaymentOrder checkPayStatus(PaymentOrder paymentOrder,String updateUser) {
    	//不是支付中的数据
    	if(PaymentOrderStatus.PAY_APPLY.notEqual(paymentOrder.getStatus())) {
    		updatePaymentOrder(paymentOrder,updateUser);
            return paymentOrder;
    	}
    	try {
    		UnionPayOrderQueryResult payResult = unionPayService.checkPayStatus(paymentOrder.getOrderNo(),DateUtil.format("yyyyMMddHHmmss", paymentOrder.getCreateTime()));
			// 检查支付订单的状态, 并生成相应记录
            PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder,PaymentOrderRecordOperType.ORDER_CHECK);
            log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(Long.parseLong(payResult.getTxnAmt()))));
            log.setTransNo(payResult.getQueryId());

            if (payResult.getTradeState().equalsIgnoreCase("USERPAYING")) {
                // 用户支付中
                log.setResultStatus(PaymentOrderRecordResultStatus.ONGOING.real());
            } else if (payResult.getTradeState().equalsIgnoreCase("SUCCESS")) {
                // 用户支付成功
                log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", payResult.getTxnTime()));
                log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
            } else {
                // 其他原因全为失败
                log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            }

//            WxPayApiData data = weiXinPayExternalService.getWxApiData();
//            if (Check.notNull(data)) {
//                log.setRequest(data.getRequestData());
//                log.setResponse(data.getResponseData());
//                log.setErrorMessage(data.getExceptionMsg());
//            }
            // 记录调用详情
            paymentOrderRecordManager.add(log, updateUser);
            
            // 更新订单
            updatePaymentOrderAndBizOrder(paymentOrder, log, updateUser);
		} catch (Exception e) {
			PaymentOrderManager.log.error("查询订单异常"+e);
		}
    	return paymentOrder;
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public boolean refundApply(String orderNo,BigDecimal refundPrice,String updateUser) {
    	PaymentOrder paymentOrder = getByBizTypeAndOrderNo(TYPE_UNION_PAY_RETAILER_ORDER,orderNo);
    	BizAssert.isTrue(Check.notNull(paymentOrder),ResultCode.BIZ.build(10), "未找到支付订单，无法发起退款");
    	// 已支付或失败才能发起
        BizAssert.isTrue(PaymentOrderStatus.PAY_SUCCEED.equal(paymentOrder.getStatus())
            || PaymentOrderStatus.REFUND_FAILED.equal(paymentOrder.getStatus()), ResultCode.BIZ.build(11), "订单必须已支付, 才能发起退款");
        // 变更状态为发起退款
        paymentOrder.setStatus(PaymentOrderStatus.REFUND_APPLY.real());
        paymentOrder.setRefundPrice(refundPrice);
        updatePaymentOrder(paymentOrder,updateUser);
        
        // 创建日志
        PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder, PaymentOrderRecordOperType.REFUND_APPLY);
        
        try {
        	String txnAmt = BigDecimalUtil.convertToString(BigDecimalUtil.dollarToCent(refundPrice), 0);
			UnionPayRefundResult refundResult = unionPayService.refund(paymentOrder.getPaymentNo(),paymentOrder.getOrderNo(),txnAmt);
			log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(Long.parseLong(refundResult.getTxnAmt()))));
			log.setTransNo(refundResult.getQueryId());
			log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
			paymentOrder.setRefundOrderNo(refundResult.getOrderId());
			updatePaymentOrder(paymentOrder,updateUser);
		} catch (Exception e) {
			log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
			log.setErrorMessage(e.getMessage());
			paymentOrder.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
			updatePaymentOrder(paymentOrder,updateUser);
		}finally {
//			WxPayApiData data = weiXinPayExternalService.getWxApiData();
//			if(Check.notNull(data)) {
//				log.setRequest(data.getRequestData());
//                log.setResponse(data.getResponseData());
//                log.setErrorMessage(data.getExceptionMsg());
//			}
			//调用记录日志
			paymentOrderRecordManager.add(log, updateUser);
		}
        if(PaymentOrderRecordResultStatus.SUCCEED.equal(log.getResultStatus())) {
        	return true;
        }
        return false;
    }
    
    private void updatePaymentOrder(PaymentOrder paymentOrder,String updateUser) {
    	paymentOrder.setUpdateUser(updateUser);
        paymentOrder.setUpdateTime(Date.from(Instant.now()));
        dao.updateByPrimaryKeySelective(paymentOrder);
    }
    
    public void refundNotify(HttpServletRequest req, HttpServletResponse response, String createUser) throws Exception{
    	UnionPayRefundNotifyResult notifyResult = unionPayService.parseRefundNotifyResult(req,response);
		PaymentOrder paymentOrder = getByBizTypeAndPaymentNo(TYPE_UNION_PAY_RETAILER_ORDER,notifyResult.getOrigQryId());
		BizAssert.isTrue(Check.notNull(paymentOrder),ResultCode.VERIFY.build(10), "支付订单不存在");
		//检查订单支付状态，并且记录操作日志
		PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder, PaymentOrderRecordOperType.REFUND_NOTIFY);
		if(notifyResult.getStatus().equalsIgnoreCase("SUCCESS")) {
			log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(Long.valueOf(notifyResult.getTxnAmt()))));
			log.setTransNo(notifyResult.getQueryId());
			log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", notifyResult.getTxnTime()));
			log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
		}else {
			log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
		}
		paymentOrderRecordManager.add(log, createUser);
		//更新订单
		updateRefundOrderAndBizOrder(paymentOrder,log,createUser);
		//返回给银联服务器http 200  状态码
		response.getWriter().print("ok");
    }
    
    public List<PaymentOrder> queryRefundApplyTimeout(Date timeout, int size) {
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria().andStatusEqualTo(PaymentOrderStatus.REFUND_APPLY.real()).andDeletedEqualTo(
            PaymentOrder.NOT_DELETED).andUpdateTimeLessThanOrEqualTo(timeout);
        example.orderBy(Column.updateTime.asc());
        example.limit(size);
        return dao.selectByExample(example);
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public PaymentOrder checkRefundStatus(PaymentOrder paymentOrder,String updateUser) {
    	//不是退款中的数据
    	if(PaymentOrderStatus.REFUND_APPLY.notEqual(paymentOrder.getStatus())) {
            return paymentOrder;
    	}
    	try {
    		UnionPayOrderQueryResult payResult = unionPayService.checkPayStatus(paymentOrder.getRefundOrderNo(),DateUtil.format("yyyyMMddHHmmss", paymentOrder.getCreateTime()));
			// 检查支付订单的状态, 并生成相应记录
            PaymentOrderRecord log = paymentOrderRecordManager.create(paymentOrder,PaymentOrderRecordOperType.REFUND_CHECK);
            log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(Long.parseLong(payResult.getTxnAmt()))));
            log.setTransNo(payResult.getQueryId());

            if (payResult.getTradeState().equalsIgnoreCase("USERPAYING")) {
                // 退款进行中
                log.setResultStatus(PaymentOrderRecordResultStatus.ONGOING.real());
            } else if (payResult.getTradeState().equalsIgnoreCase("SUCCESS")) {
                // 用户退款成功
                log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", payResult.getTxnTime()));
                log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
            } else {
                // 其他原因全为失败
                log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            }

//            WxPayApiData data = weiXinPayExternalService.getWxApiData();
//            if (Check.notNull(data)) {
//                log.setRequest(data.getRequestData());
//                log.setResponse(data.getResponseData());
//                log.setErrorMessage(data.getExceptionMsg());
//            }
            // 记录调用详情
            paymentOrderRecordManager.add(log, updateUser);
            
            // 更新订单
            updateRefundOrderAndBizOrder(paymentOrder, log, updateUser);
		} catch (Exception e) {
			PaymentOrderManager.log.error("查询订单异常"+e);
		}
    	return paymentOrder;
    }
    
    public void updatePaymentOrderAndBizOrder(PaymentOrder paymentOrder,PaymentOrderRecord log,String updateUser) {
    	//支付成功
    	if(PaymentOrderRecordResultStatus.SUCCEED.real().equals(log.getResultStatus())) {
    		paymentOrder.setPaymentNo(log.getTransNo());
    		paymentOrder.setPaymentTime(Date.from(Instant.now()));
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_SUCCEED.real());
    		updatePaymentOrder(paymentOrder,updateUser);
    		
    	}else {//支付失败
    		paymentOrder.setStatus(PaymentOrderStatus.PAY_FAILED.real());
    		updatePaymentOrder(paymentOrder,updateUser);
    	}
    	// 通知业务订单
    	if(paymentOrder.getBizType().equals(TYPE_UNION_PAY_RETAILER_ORDER)) {
    		directMailGoodsOrderManager.paySuccess(paymentOrder, updateUser);
    	}
    }
    
    public void updateRefundOrderAndBizOrder(PaymentOrder paymentOrder,PaymentOrderRecord log,String updateUser) {
    	//退款成功
    	if(PaymentOrderRecordResultStatus.SUCCEED.real().equals(log.getResultStatus())) {
    		paymentOrder.setStatus(PaymentOrderStatus.REFUND_SUCCEED.real());
    		updatePaymentOrder(paymentOrder,updateUser);
    	}else {//退款失败
    		paymentOrder.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
    		updatePaymentOrder(paymentOrder,updateUser);
    	}
    }
    
    public void payFrontNotify(HttpServletRequest req, HttpServletResponse response) throws Exception{
    	unionPayService.payFrontNotify(req, response);
    }
    
    public PaymentOrder getByBizTypeAndOrderNo(String bizType, String orderNo) {
    	PaymentOrderExample example = new PaymentOrderExample();
    	Criteria criteria = example.createCriteria();
    	criteria.andBizTypeEqualTo(bizType).andOrderNoEqualTo(orderNo).andDeletedEqualTo(PaymentOrder.NOT_DELETED);
    	return dao.selectOneByExample(example);
    }
    
    public PaymentOrder getByBizTypeAndPaymentNo(String bizType, String paymentNo) {
    	PaymentOrderExample example = new PaymentOrderExample();
    	Criteria criteria = example.createCriteria();
    	criteria.andBizTypeEqualTo(bizType).andPaymentNoEqualTo(paymentNo).andDeletedEqualTo(PaymentOrder.NOT_DELETED);
    	return dao.selectOneByExample(example);
    }
    
    public PaymentOrder getByOrderNo(String orderNo) {
    	PaymentOrderExample example = new PaymentOrderExample();
    	Criteria criteria = example.createCriteria();
    	criteria.andOrderNoEqualTo(orderNo).andDeletedEqualTo(PaymentOrder.NOT_DELETED);
    	return dao.selectOneByExample(example);
    }
    
    public PaymentOrder getById(Integer paymentOrderId) {
        return dao.selectByPrimaryKey(paymentOrderId);
    }
    
    public Pagination<PaymentOrder> queryByQuery(QueryPaymentOrderDTO query, Sorting[] sorts, Page page) {
        return Pagination.create(page,
            dao.countByQuery(query),
            () -> dao.queryByQuery(query, Sortings.builder(sorts).verify(Column::valueOf).toSql(), page));
    }
}